#region Using directives
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
#endregion

namespace SharpWind.Input
{
	public class InputManager
	{
		#region Fields

		private Dictionary<string, InputControl> controls = new Dictionary<string, InputControl>();

		private Dictionary<string, float> pressStamps = new Dictionary<string, float>();

		private GamePadState gamePadState;

		private KeyboardState keyboardState;

		private MouseState mouseState;

		private int lastScrollWheelValue = 0;

		private int currentScrollWheelValue = 0;

		#endregion

		#region Constructors

		internal InputManager()
		{
		}

		#endregion

		#region Methods

		internal void Update()
		{
			this.gamePadState	= GamePad.GetState( PlayerIndex.One );
			this.keyboardState	= Keyboard.GetState();
			this.mouseState		= Mouse.GetState();
			this.currentScrollWheelValue = this.mouseState.ScrollWheelValue - this.lastScrollWheelValue;
			this.lastScrollWheelValue	 = this.mouseState.ScrollWheelValue;
		}

		public float ControlState( string control )
		{
			return ControlState( control, false );
		}

		public float ControlState( string control, bool ignorePressStamps )
		{
			float state = 0f;

			if( !string.IsNullOrEmpty( control ) && this.controls.ContainsKey( control ) )
			{
				InputSource source	= this.controls[control].Source;
				InputType type		= this.controls[control].Type;

				switch( source )
				{
					case InputSource.Keyboard:
					{
						state = ( this.keyboardState.IsKeyDown( this.controls[control].Keys ) ) ? 1f : 0f;
						break;
					}

					case InputSource.Mouse:
					{
						switch( this.controls[control].Mouse )
						{
							case MouseInput.X:
								state = (float)this.mouseState.X / Engine.Instance.Window.ClientBounds.Width;
								break;

							case MouseInput.Y:
								state = (float)this.mouseState.Y / Engine.Instance.Window.ClientBounds.Height;
								break;

							case MouseInput.LeftButton:
								state = ( this.mouseState.LeftButton == ButtonState.Pressed ) ? 1f : 0f;
								break;

							case MouseInput.MiddleButton:
								state = ( this.mouseState.MiddleButton == ButtonState.Pressed ) ? 1f : 0f;
								break;

							case MouseInput.RightButton:
								state = ( this.mouseState.RightButton == ButtonState.Pressed ) ? 1f : 0f;
								break;

							case MouseInput.XButton1:
								state = ( this.mouseState.XButton1 == ButtonState.Pressed ) ? 1f : 0f;
								break;

							case MouseInput.XButton2:
								state = ( this.mouseState.XButton2 == ButtonState.Pressed ) ? 1f : 0f;
								break;

							case MouseInput.ScrollWheel:
								if( type == InputType.Analogue )
								{
									state = (float)currentScrollWheelValue;
								}
								else
								{
									state = ( currentScrollWheelValue > 0f ) ? 1f : ( currentScrollWheelValue < 0f ) ? -1f : 0f;
								}
								break;
							
						}
						break;
					}

					case InputSource.GamePad:
					{
						if( this.gamePadState.IsConnected )
						{
							switch( this.controls[control].GamePad )
							{
								case GamePadInput.A:
									state = ( this.gamePadState.Buttons.A == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.B:
									state = ( this.gamePadState.Buttons.B == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.X:
									state = ( this.gamePadState.Buttons.X == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Y:
									state = ( this.gamePadState.Buttons.Y == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Start:
									state = ( this.gamePadState.Buttons.Start == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Back:
									state = ( this.gamePadState.Buttons.Back == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Left:
									state = ( this.gamePadState.DPad.Left == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Right:
									state = ( this.gamePadState.DPad.Right == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Up:
									state = ( this.gamePadState.DPad.Up == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.Down:
									state = ( this.gamePadState.DPad.Down == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.LeftTrigger:
									if( type == InputType.Analogue )
									{
										state = this.gamePadState.Triggers.Left;
									}
									else
									{
										state = ( this.gamePadState.Triggers.Left > 0f ) ? 1f : 0f;
									}
									break;

								case GamePadInput.RightTrigger:
									if( type == InputType.Analogue )
									{
										state = this.gamePadState.Triggers.Right;
									}
									else
									{
										state = ( this.gamePadState.Triggers.Right > 0f ) ? 1f : 0f;
									}
									break;

								case GamePadInput.LeftBumper:
									state = ( this.gamePadState.Buttons.LeftShoulder == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.RightBumper:
									state = ( this.gamePadState.Buttons.RightShoulder == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.LeftStick:
									state = ( this.gamePadState.Buttons.LeftStick == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.RightStick:
									state = ( this.gamePadState.Buttons.RightStick == ButtonState.Pressed ) ? 1f : 0f;
									break;

								case GamePadInput.ThumbstickLeftX:
									state = this.gamePadState.ThumbSticks.Left.X;
									break;

								case GamePadInput.ThumbstickLeftY:
									state = this.gamePadState.ThumbSticks.Left.Y * -1f;
									break;

								case GamePadInput.ThumbstickRightX:
									state = this.gamePadState.ThumbSticks.Right.X;
									break;

								case GamePadInput.ThumbstickRightY:
									state = this.gamePadState.ThumbSticks.Right.Y * -1f;
									break;
							}
						}
						break;
					}
				}
			}

			if( this.pressStamps.ContainsKey( control ) && ignorePressStamps == false )
			{
				if( this.pressStamps[control] == state )
				{
					return 0f;
				}
				else
				{
					this.pressStamps[control] = state;
					return state;
				}

			}

			return state;
		}

		public bool RegisterControl( string name, InputType type, MouseInput mouse )
		{
			if( string.IsNullOrEmpty( name ) || !IsValid( type, mouse ) || this.controls.ContainsKey( name ) )
			{
				return false;
			}

			this.controls.Add( name, new InputControl( type, InputSource.Mouse, mouse ) );

			if( type == InputType.Digital )
			{
				this.pressStamps.Add( name, 0f );
			}

			return true;
		}

		public bool RegisterControl( string name, InputType type, GamePadInput gamepad )
		{
			if( string.IsNullOrEmpty( name ) || !IsValid( type, gamepad ) || this.controls.ContainsKey( name ) )
			{
				return false;
			}

			this.controls.Add( name, new InputControl( type, InputSource.GamePad, gamepad ) );

			if( type == InputType.Digital )
			{
				this.pressStamps.Add( name, 0f );
			}

			return true;
		}

		public bool RegisterControl( string name, InputType type, Keys keys )
		{
			if( string.IsNullOrEmpty( name ) || type == InputType.Analogue || this.controls.ContainsKey( name ) )
			{
				return false;
			}

			this.controls.Add( name, new InputControl( type, InputSource.Keyboard, keys ) );

			if( type == InputType.Digital )
			{
				this.pressStamps.Add( name, 0f );
			}

			return true;
		}

		public void ClearMappings()
		{
			this.controls.Clear();
			this.pressStamps.Clear();
		}

		private bool IsValid( InputType type, GamePadInput gamePad )
		{
			bool result = false;

			switch( type )
			{
				case InputType.Analogue:
				{
					if( gamePad == GamePadInput.ThumbstickLeftX || gamePad == GamePadInput.ThumbstickLeftY ||
						gamePad == GamePadInput.ThumbstickRightX || gamePad == GamePadInput.ThumbstickRightY ||
						gamePad == GamePadInput.LeftTrigger || gamePad == GamePadInput.RightTrigger )
					{
						result = true;
					}
					break;
				}

				case InputType.Digital:
				{
					if( gamePad == GamePadInput.A || gamePad == GamePadInput.B || 
						gamePad == GamePadInput.X || gamePad == GamePadInput.Y ||
						gamePad == GamePadInput.Left || gamePad == GamePadInput.Right || 
						gamePad == GamePadInput.Up || gamePad == GamePadInput.Down ||
						gamePad == GamePadInput.Start || gamePad == GamePadInput.Back || 
						gamePad == GamePadInput.LeftTrigger || gamePad == GamePadInput.RightTrigger || 
						gamePad == GamePadInput.LeftBumper || gamePad == GamePadInput.RightBumper || 
						gamePad == GamePadInput.LeftStick || gamePad == GamePadInput.RightStick )
					{
						result = true;
					}
					break;
				}
			}

			return result;
		}

		private bool IsValid( InputType type, MouseInput mouse )
		{
			bool result = false;

			switch( type )
			{
				case InputType.Analogue:
				{
					if( mouse == MouseInput.X || mouse == MouseInput.Y || mouse == MouseInput.ScrollWheel )
					{
						result = true;
					}
					break;
				}

				case InputType.Digital:
				{
					if( mouse == MouseInput.LeftButton || mouse == MouseInput.MiddleButton ||
						mouse == MouseInput.RightButton || mouse == MouseInput.XButton1 ||
						mouse == MouseInput.XButton2 || mouse == MouseInput.ScrollWheel )
					{
						result = true;
					}
					break;
				}
			}

			return result;
		}

		#endregion
	}
}
